#include "demo04.h"

class LogLevel{
public:
    enum Level{
        UNKNOW = 0, //起手先来个未知级别兜底
        DEBUG = 1,  //调试级别
        INFO = 2,   //普通信息级别
        WARN = 3,   //警告信息
        ERROR = 4,  //错误信息
        FATAL = 5   //灾难级信息
    };

    static const char* ToString(LogLevel::Level level);
};
const char* LogLevel::ToString(LogLevel::Level level){
    switch(level) {
#define XX(name) \
    case LogLevel::name: \
        return #name; \
        break;
        
    XX(DEBUG);
    XX(INFO);
    XX(WARN);
    XX(ERROR);
    XX(FATAL);
    
#undef XX
    default:
        return "UNKNOW";
    }
    return "UNKNOW";
}

class LogEvent {
public:
    typedef std::shared_ptr<LogEvent> ptr;
    LogEvent(const std::string& logName,LogLevel::Level level
            ,const char* file, int32_t m_line, uint32_t elapse
            , uint32_t thread_id, uint32_t fiber_id, uint64_t time);

    const char* getFile() const { return m_file;}
    int32_t getLine() const { return m_line;}
    uint32_t getElapse() const { return m_elapse;}
    uint32_t getThreadId() const { return m_threadId;}
    uint32_t getFiberId() const { return m_fiberId;}
    uint64_t getTime() const { return m_time;}
    LogLevel::Level getLevel() const { return m_level;}
    const std::string& getLogName() { return m_logName;}
    std::string getContext() const {return m_ss.str();}
    std::stringstream& getSS() {return m_ss;}

private:
    LogLevel::Level m_level;       //日志级别           
    const char* m_file = nullptr;  //文件名
    int32_t m_line = 0;            //行号
    uint32_t m_elapse = 0;         //程序启动开始到现在的毫秒数
    uint32_t m_threadId = 0;       //线程id
    uint32_t m_fiberId = 0;        //协程id
    uint64_t m_time = 0;           //时间戳
    std::string m_logName;         //名字
    std::stringstream m_ss;        //字符流

};

LogEvent::LogEvent(const std::string& logName, LogLevel::Level level
            ,const char* file, int32_t line, uint32_t elapse
            , uint32_t thread_id, uint32_t fiber_id, uint64_t time)
    :m_logName(logName),m_level(level)
    ,m_file(file)
    ,m_line(line)
    ,m_elapse(elapse)
    ,m_threadId(thread_id)
    ,m_fiberId(fiber_id)
    ,m_time(time){
}

class LogFormatter{
public:
    typedef std::shared_ptr<LogFormatter> ptr;
    LogFormatter(const std::string& patter);

    void init();
    std::string format(LogEvent::ptr event);

    class FormatItem{
    public:
        typedef std::shared_ptr<FormatItem> ptr;
        virtual ~FormatItem() {}
        virtual void format(std::ostream& os, LogEvent::ptr event) = 0;
    };

private:
    std::string m_patter;
    std::vector<FormatItem::ptr> m_items; 
};

class LogAppender{
public:
    //定义智能指针
    typedef std::shared_ptr<LogAppender> ptr;
    typedef CASLock MutexType;
    //虚析构 空函数没有复杂逻辑所以 直接定义掉
    virtual ~LogAppender(){}
    //输出函数为纯虚函数，因为具体实现各个子类不一样，由各个子类自己决定
    virtual void log(LogEvent::ptr event) = 0;

    void setFormatter(LogFormatter::ptr val) { 
        MutexType::Lock lock(m_mutex);
        m_formatter = val;
    }
    LogFormatter::ptr getFormatter(){
        MutexType::Lock lock(m_mutex);
        return m_formatter;
    }

    LogFormatter::ptr m_formatter;

protected:
    MutexType m_mutex;

};


class FileLogAppender : public LogAppender {
public:
    typedef std::shared_ptr<FileLogAppender> ptr;
    FileLogAppender(const std::string& filename);
    void log(LogEvent::ptr event) override;

    bool reopen();
private:
    std::string m_filename;
    std::ofstream m_filestream;
};

bool FileLogAppender::reopen(){
    MutexType::Lock lock(m_mutex);
    if(m_filestream){
        m_filestream.close();
    }
    m_filestream.open(m_filename, std::ios::app);
    return !!m_filestream;
}

//输出到控制台的Appender
class StdoutLogAppender : public LogAppender {
public:
    typedef std::shared_ptr<StdoutLogAppender> ptr;
    //这里的override用于表示是重写父类方法的
    void log(LogEvent::ptr event) override;
};


class Logger{
public:
    //定义智能指针
    typedef std::shared_ptr<Logger> ptr;
    typedef CASLock MutexType;
    //日志名称有且仅有构造时可以指定，如果未指定则给个默认名称 root 
    //这里传引用类型是为了避免不必要的拷贝操作
    //使用const是为了规定这个名称在内部不再会被改变
    Logger(const std::string& name = "root");
    
    //希望名称能被获取 但不应该被改变所以用const 为避免无用的拷贝所以返回引用类型
    const std::string& getName() const { return m_name; }
    LogLevel::Level getLevel() const { return m_level; }
    void setLevel(LogLevel::Level val) {m_level = val;}
    
    //定义了一个输出日志的方法 传入想要查看的最大日志级别
    void log(LogEvent::ptr event);
    //新增一个适配器
    void addAppender(LogAppender::ptr appender);  
    //删除一个适配器  
    void delAppender(LogAppender::ptr appender);
private:
    //这里使用 m_ 开头是一个私有变量的规范
    std::string m_name;
    //日志器能输出的最大日志级别
    LogLevel::Level m_level;
    //Appender集合
    std::list<LogAppender::ptr> m_appenders;
    /// 日志格式器
    LogFormatter::ptr m_formatter;
    /// 主日志器
    Logger::ptr m_root;

    MutexType m_mutex;
};

//头文件中已经有默认值定义了 这里就不需要了(也就是不需要 nmae="root")，否则编译器会报错
Logger::Logger(const std::string& name)
    :m_name(name)
    //这里指定日志器一个自身默认级别是DEBUG
    ,m_level(LogLevel::DEBUG){
    m_formatter.reset(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
}
void Logger::addAppender(LogAppender::ptr appender) {
    MutexType::Lock lock(m_mutex);
    m_appenders.push_back(appender);
}

void Logger::delAppender(LogAppender::ptr appender) {
    MutexType::Lock lock(m_mutex);
    for(auto it = m_appenders.begin();
            it != m_appenders.end(); ++it) {
        if(*it == appender) {
            m_appenders.erase(it);
            break;
        }
    }
}
void Logger::log(LogEvent::ptr event){
    //如果想要查看的级别大于等于当前日志器能查看的级别，那么才进行输出
    if(event->getLevel() >= m_level){
        MutexType::Lock lock(m_mutex);
        for(auto& i : m_appenders) {
            i->log(event);
        }
    }
}

LogFormatter::LogFormatter(const std::string& patter):m_patter(patter){
    init();
}


class MessageFormatItem:public LogFormatter::FormatItem{
public:
    MessageFormatItem(const std::string& str = ""){}
    void format(std::ostream& os, LogEvent::ptr event) override{
        os<<event->getContext();
    }
};

class LevelFormatItem : public LogFormatter::FormatItem {
public:
    LevelFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << LogLevel::ToString(event->getLevel());
    }
};

class ElapseFormatItem : public LogFormatter::FormatItem {
public:
    ElapseFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << event->getElapse();
    }
};

class NameFormatItem : public LogFormatter::FormatItem {
public:
    NameFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << event->getLogName();
    }
};

class ThreadIdFormatItem : public LogFormatter::FormatItem {
public:
    ThreadIdFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << event->getThreadId();
    }
};

class FiberIdFormatItem : public LogFormatter::FormatItem {
public:
    FiberIdFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << event->getFiberId();
    }
};

class DateTimeFormatItem : public LogFormatter::FormatItem {
public:
    DateTimeFormatItem(const std::string& format = "%Y-%m-%d %H:%M:%S")
        :m_format(format) {
        if(m_format.empty()) {
            m_format = "%Y-%m-%d %H:%M:%S";
        }
    }

    void format(std::ostream& os, LogEvent::ptr event) override {
        struct tm tm;
        time_t time = event->getTime();
        localtime_r(&time, &tm);
        char buf[64];
        strftime(buf, sizeof(buf), m_format.c_str(), &tm);
        os << buf;
    }
private:
    std::string m_format;
};

class FilenameFormatItem : public LogFormatter::FormatItem {
public:
    FilenameFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << event->getFile();
    }
};

class LineFormatItem : public LogFormatter::FormatItem {
public:
    LineFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << event->getLine();
    }
};

class NewLineFormatItem : public LogFormatter::FormatItem {
public:
    NewLineFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << std::endl;
    }
};

class StringFormatItem : public LogFormatter::FormatItem {
public:
    StringFormatItem(const std::string& str)
        :m_string(str) {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << m_string;
    }
private:
    std::string m_string;
};

class TabFormatItem : public LogFormatter::FormatItem {
public:
    TabFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, LogEvent::ptr event) override {
        os << "\t";
    }
private:
    std::string m_string;
};

void LogFormatter::init(){
    std::vector<std::tuple<std::string,std::string,int>> vec;//元组中的内容是<符号，子串，类型>
    std::string nstr; //解析后的字符串
    //循环解析
    for(int i = 0;i < m_patter.size();i ++){
        if(m_patter[i] != '%'){
            nstr.append(1, m_patter[i]);
            continue;
        }
        if((i+1)<m_patter.size()){
            if(m_patter[i+1] == '%'){
                nstr.append(1, m_patter[i+1]);
                continue;
            }
        }
        size_t n = i+1; //跳过%从下一个字符开始解析
        int fmt_status = 0; //已经遇到{但是还没遇到}时，值为1
        size_t fmt_begin = 0; //表示大括号开始的位置

        std::string str;
        std::string fmt;    //存放在{}中间的字符
        //从i+1开始遍历
        while(n < m_patter.size()){
            if(!fmt_status && (!isalpha(m_patter[n]) && m_patter[n] != '{' && m_patter[n] != '}')){
                str = m_patter.substr(i + 1, n-i-1);
                break;
            }
            if(fmt_status==0){
                if(m_patter[n] == '{'){
                    //遇到'{',将前面的字符截取
                    str = m_patter.substr(i+1, n-i-1);
                    //std::cout<<"str:"<<str<<std::endl;
                    fmt_status = 1;
                    fmt_begin = n;
                    ++n;
                    continue;
                }
            }else if(fmt_status == 1){
                if(m_patter[n] == '}'){
                    fmt = m_patter.substr(fmt_begin+1, n-fmt_begin-1);
                    //std::cout<<"fmt:"<<fmt<<std::endl;
                    fmt_status = 0;
                    ++n;
                    break;
                }
            }

            ++n;

            //判断遍历是否结束
            if(n == m_patter.size()){
                if(str.empty()){
                    str = m_patter.substr(i+1);
                    //std::cout<<"end str:"<<str<<std::endl;
                }
            }
        }

        if(fmt_status == 0){
            if(!nstr.empty()){
                vec.push_back(std::make_tuple(nstr, std::string(), 0));
                nstr.clear();
            }
            //fmt:寻找到的格式
            vec.push_back(std::make_tuple(str, fmt, 1));
            //std::cout<<"nstr:"<<nstr<<std::endl;
            i = n-1;
        }else if(fmt_status == 1){
            //没有找到与{相匹配的}，格式错误
            std::cout << "pattern parse error: " << m_patter << " - " << m_patter.substr(i) << std::endl;
            vec.push_back(std::make_tuple("<<pattern_error>>", fmt, 0));
        }
    }

    if(!nstr.empty()){
        vec.push_back(std::make_tuple(nstr, "", 0));
    }

    static std::map<std::string, std::function<FormatItem::ptr(const std::string& str)>> s_format_items = {
#define XX(str, C) \
        {#str, [](const std::string& fmt) { return FormatItem::ptr(new C(fmt));}}

        XX(m, MessageFormatItem),
        XX(p, LevelFormatItem),
        XX(r, ElapseFormatItem),
        XX(c, NameFormatItem),
        XX(t, ThreadIdFormatItem),
        XX(n, NewLineFormatItem),
        XX(d, DateTimeFormatItem),
        XX(f, FilenameFormatItem),
        XX(l, LineFormatItem),
        XX(T, TabFormatItem),
        XX(F, FiberIdFormatItem),
#undef XX
    };

    for(auto &i : vec){
        if(std::get<2>(i) == 0){
            m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(i))));
        }else{
            auto it = s_format_items.find(std::get<0>(i));
            if(it == s_format_items.end()){
                m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %" + std::get<0>(i))));
            }else{
                m_items.push_back(it->second(std::get<1>(i)));
            }
        }
    }
}

std::string LogFormatter::format(LogEvent::ptr event){
    std::stringstream ss;
    for(auto i : m_items){
        i->format(ss, event);
    }
    return ss.str();
}

void StdoutLogAppender::log(LogEvent::ptr event){
    MutexType::Lock lock(m_mutex);
    std::cout<<m_formatter->format(event);
}

void FileLogAppender::log(LogEvent::ptr event) {
    if(reopen()){
        m_filestream<<m_formatter->format(event);
    }
}

FileLogAppender::FileLogAppender(const std::string& filename)
    :m_filename(filename) {
    
}

class LogEventWrap{
public:
    LogEventWrap(Logger::ptr logger, LogEvent::ptr e);
    ~LogEventWrap();
    LogEvent::ptr getEvent() { return m_event;}
    std::stringstream &getSS();

private:
    LogEvent::ptr m_event;
    Logger::ptr m_logger;
};

LogEventWrap::LogEventWrap(Logger::ptr logger, LogEvent::ptr e):m_event(e),m_logger(logger){

}

LogEventWrap::~LogEventWrap(){
    m_logger->log(m_event);
}

std::stringstream& LogEventWrap::getSS(){
    return m_event->getSS();
}

class LoggerManager{
public:
    typedef CASLock MutexType;

    static LoggerManager& getInstance(){
        static LoggerManager instance;
        return instance;
    }
    LoggerManager();
    Logger::ptr getLogger(const std::string& name);

    void init();
    Logger::ptr getRoot() const {return m_root;};

private:
    std::map<std::string, Logger::ptr> m_loggers;
    Logger::ptr m_root;

    MutexType m_mutex;
};

//typedef Singleton<LoggerManager> loggerMrg;

LoggerManager::LoggerManager(){
    m_root.reset(new Logger);
    m_root->addAppender(LogAppender::ptr(new StdoutLogAppender));
}

Logger::ptr LoggerManager::getLogger(const std::string& name){
    MutexType::Lock lock(m_mutex);
    auto it = m_loggers.find(name);
    return it == m_loggers.end() ? m_root:it->second;
}


#define LOG_LEVEL(logger, level) \
    if(logger->getLevel()<= level)  \
        LogEventWrap(logger, LogEvent::ptr(new LogEvent(logger->getName(), level, __FILE__, __LINE__, 0 \
        , sylar::GetThreadId(), sylar::GetFiberId(), time(0)))).getSS()

#define SYLAR_LOG_DEBUG(logger) LOG_LEVEL(logger, LogLevel::DEBUG)

#define SYLAR_LOG_INFO(logger) LOG_LEVEL(logger, LogLevel::INFO)

#define SYLAR_LOG_WARN(logger) LOG_LEVEL(logger, LogLevel::WARN)

#define SYLAR_LOG_ERROR(logger) LOG_LEVEL(logger, LogLevel::ERROR)

#define SYLAR_LOG_FATAL(logger) LOG_LEVEL(logger, LogLevel::FATAL)

#define SYLAR_LOG_ROOT() LoggerManager::getInstance()->getRoot()


Logger::ptr createLogger(){
    Logger::ptr lg(new Logger("zgh"));
    LogFormatter::ptr formatter(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
    StdoutLogAppender::ptr stdApd(new StdoutLogAppender());
    stdApd->setFormatter(formatter);
    lg->addAppender(stdApd);
    return lg;
}


#if 0
//此时我们可以写一个测试类试试
int main(int argc,char** argv){
    std::cout<<"===start==="<<std::endl;
    Logger::ptr lg(new Logger("XYZ"));
    //创建一个日志事件(这里的内容随便定义，因为我们没有真正用到它)
    LogEvent::ptr event(new LogEvent(lg->getName(),
        LogLevel::INFO,     //日志级别
        __FILE__,           //文件名称
        __LINE__,           //行号
        1234567,            //运行时间
        syscall(SYS_gettid),//线程ID
        0,                  //协程ID
        time(0)             //当前时间
    ));
    LogFormatter::ptr formatter(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
    //添加控制台输出适配器
    StdoutLogAppender::ptr stdApd(new StdoutLogAppender());
    stdApd->setFormatter(formatter);
    lg->addAppender(stdApd);
    LogEventWrap(lg, event).getSS()<<" 追加内容";   //使用析构函数
    LOG_LEVEL(lg, LogLevel::INFO)<<"message";   //调用弘
    SYLAR_LOG_DEBUG(lg)<<"你好";    //调用弘

    //添加文件输出适配器
    FileLogAppender::ptr fileAP(new FileLogAppender("log.txt"));
    fileAP->setFormatter(formatter);
    lg->addAppender(fileAP);
    LOG_LEVEL(lg, LogLevel::INFO)<<"hello log";
    SYLAR_LOG_DEBUG(lg) << "hello debug";


    std::cout<<"===end==="<<std::endl;
    return 0;
}
#endif
